/*************************************************
 * 
 * Copyright by Bruno Eberhard
 * 
 * Created on 29.07.2005
 *
 *************************************************/
package fightplan.algorithmen.ko;

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.LayoutManager;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;

import fightplan.business.Fight;
import fightplan.business.FightEntry;
import fightplan.business.FighterHolder;
import fightplan.business.FighterProvider;
import fightplan.business.Result;

@SuppressWarnings("serial")
public class KOPanel extends JPanel implements LayoutManager, Serializable {
	private static final int labelPaddingX = 30;

	private Fight rootFight;
	private int depth;
	private List<Fight> upperTrostRundenFights;
	private List<Fight> lowerTrostRundenFights;

	private transient JComponent[] labels;
	private transient JComponent[] labelsUpperTrostRunde;
	private transient JComponent[] labelsLowerTrostRunde;
	private transient ArrayList<Component> connections;

	private transient JLabel labelFinal;
	private transient JLabel labelUpperTrostrunde;
	private transient JLabel labelLowerTrostrunde;

	private boolean isEndPanel = false;
	private boolean isEndPanelOfFinalrunde = false;

	KOPanel(Fight rootFight, int depth) {
		this(rootFight, depth, null, null);
	}

	KOPanel(Fight rootFight, int depth, boolean isEndPanelOfFinalrunde) {
		this(rootFight, depth, null, null, isEndPanelOfFinalrunde);
	}

	public KOPanel(Fight rootFight, int depth, List<Fight> upperTrostRundenFights, List<Fight> lowerTrostRundenFights) {
		this(rootFight, depth, upperTrostRundenFights, lowerTrostRundenFights, false);
	}

	public KOPanel(Fight rootFight, int depth, List<Fight> upperTrostRundenFights, List<Fight> lowerTrostRundenFights,
			boolean isEndPanelOfFinalrunde) {
		super();
		this.rootFight = rootFight;
		this.depth = depth;
		this.upperTrostRundenFights = upperTrostRundenFights;
		this.lowerTrostRundenFights = lowerTrostRundenFights;
		this.isEndPanelOfFinalrunde = isEndPanelOfFinalrunde;
		init();
	}

	private void init() {
		setPreferredSize(new Dimension(800, 500));
		setLayout(this);
		setOpaque(true);

		// normal round
		int count = 1;
		for (int i = 0; i <= depth; i++) {
			count *= 2;
		} // count = 2^depth - 1;
		count -= 1;
		labels = new JComponent[count];
		buildTree(rootFight, labels, false);

		add(labelFinal = new JLabel("Finalist"));
		labelFinal.setVisible(false);

		// connections normal round
		int pos = 0;
		connections = new ArrayList<Component>();

		if (!isEndPanelOfFinalrunde) {
			Component c = new Connection2(new JComponent[] { labels[pos * 2 + 1], labels[pos * 2 + 2], labels[pos] });
			add(c, null);
			connections.add(c);
		} else {
			labels[0].setVisible(false);
		}

		pos++;
		int ex = 2;
		for (int i = 2; i <= depth; i++) {
			for (int j = 0; j < ex; j++) {
				Component c = new Connection1(
						new JComponent[] { labels[pos * 2 + 1], labels[pos * 2 + 2], labels[pos] });
				add(c, null);
				connections.add(c);
				pos++;
			}
			ex = ex * 2;
		}

		// Trostrunde

		if (upperTrostRundenFights != null) { // &&
												// !upperTrostRundenFights.isEmpty()
			labelsUpperTrostRunde = new JLabel[upperTrostRundenFights.size() * 2 + 1];
			buildTrostRunde(upperTrostRundenFights, labelsUpperTrostRunde);
			add(labelUpperTrostrunde = new JLabel("Trostrunde"));
		}
		if (lowerTrostRundenFights != null) { // &&
												// !lowerTrostRundenFights.isEmpty()
			labelsLowerTrostRunde = new JLabel[lowerTrostRundenFights.size() * 2 + 1];
			buildTrostRunde(lowerTrostRundenFights, labelsLowerTrostRunde);
			add(labelLowerTrostrunde = new JLabel("Trostrunde"));
		}

	}

	private void buildTrostRunde(List<Fight> trostRundenFights, JComponent[] labels) {
		buildTree(trostRundenFights.get(trostRundenFights.size() - 1), labels, true);

		// connections trotstrunde
		int pos = 3;
		JComponent c = new Connection2(new JComponent[] { labels[pos - 2], labels[pos - 1], labels[pos - 3] });
		add(c, null);
		connections.add(c);
		for (int i = 0; i < trostRundenFights.size() - 1; i++) {
			c = new Connection1(new JComponent[] { labels[pos + 1], labels[pos], labels[pos - 1] });
			add(c, null);
			connections.add(c);
			pos += 2;
		}
	}

	public JComponent getRootLabel() {
		return labels[0];
	}

	private JComponent buildTree(Fight rootFight, JComponent[] labels, boolean trostrunde) {
		return buildTree(rootFight.getWinResultHolder(), 0, labels, true, trostrunde);
	}

	private JComponent buildTree(FighterProvider fighterProvider, int pos, JComponent[] labels, boolean upper,
			boolean trostrunde) {
		if (pos >= labels.length)
			return null;
		int pos1 = trostrunde ? pos + 1 : pos * 2 + 1;
		int pos2 = trostrunde ? pos + 2 : pos * 2 + 2;
		if (fighterProvider != null) {
			labels[pos] = new FighterLabel(fighterProvider, pos >= 15); // TODO
																		// check
																		// for
																		// depth,
																		// not
																		// 15!
																		// (es
																		// geht
																		// dabei
																		// darum,
																		// ob
																		// das
																		// Dojo
																		// angezeigt
																		// werden
																		// soll)

			if (fighterProvider instanceof FighterHolder) {
				Fight fight = ((FighterHolder) fighterProvider).getFight();
				if (!trostrunde) {
					for (int i = 0; i < 2; i++) {
						FightEntry entry = fight.getFightEntries().get(upper ? i : 1 - i);
						int nextPos = pos * 2 + 1 + i;
						JComponent c = buildTree(entry.getFighterProvider(), nextPos, labels, i == 0, trostrunde);
						if (c != null)
							c.addMouseListener(new WinMouseListener(fight, entry));
					}
				} else {
					for (int i = 0; i < 2; i++) {
						FightEntry entry = fight.getFightEntries().get(i);
						int nextPos = pos + 1 + i;
						JComponent c = buildTree(entry.getFighterProvider(), nextPos, labels, i == 0, trostrunde);
						if (c != null)
							c.addMouseListener(new WinMouseListener(fight, entry));
					}
				}
			} else {
				// Wir sind in einem Leaf gelandet.
				if (upper) {
					buildTree(fighterProvider.getFighter(), pos1, labels, true, trostrunde);
					buildTree(null, pos2, labels, false, trostrunde);
				} else {
					buildTree(null, pos1, labels, true, trostrunde);
					buildTree(fighterProvider.getFighter(), pos2, labels, false, trostrunde);
				}
			}
		} else {
			labels[pos] = new FighterLabel(null);
			buildTree(null, pos1, labels, true, trostrunde);
			buildTree(null, pos2, labels, false, trostrunde);
		}
		add(labels[pos]);
		return labels[pos];
	}

	// MouseListener

	static class WinMouseListener extends MouseAdapter {
		private Fight fight;
		private FightEntry fighter;

		public WinMouseListener(Fight fight, FightEntry fighter) {
			this.fight = fight;
			this.fighter = fighter;
		}

		@Override
		public void mousePressed(MouseEvent e) {
			for (FightEntry entry : fight.getFightEntries()) {
				if (entry.equals(this.fighter)) {
					entry.setResult(Result.WON);
				} else {
					entry.setResult(Result.LOST);
				}
			}
		}

	}

	// LayoutManager

	public void addLayoutComponent(String name, Component comp) {
	}

	private static final Dimension MIN_SIZE = new Dimension(450, 300);

	public Dimension minimumLayoutSize(Container parent) {
		return MIN_SIZE;
	}

	public Dimension preferredLayoutSize(Container parent) {
		return this.getPreferredSize();
	}

	public void removeLayoutComponent(Component comp) {
	}

	public void layoutContainer(Container parent) {
		final int borderLeftRight = 0;
		int labelHeight = FighterLabel.getLabelHeight();

		// the drawing is divided in "column"
		// a name field occupies

		int columnWidth = (parent.getWidth() - borderLeftRight * 2 - FighterDojoLabel.dojoNameSize)
				/ (Math.max(depth, 3) - 0);
		int labelWidth = columnWidth - labelPaddingX;

		int xPos = parent.getWidth() - labelWidth - borderLeftRight;
		int labelsPerColumn = 1;
		int labelInColumn = 0;
		int yPos = -labelHeight / 2;
		int yOffset = 0;

		int normalRoundHeight = parent.getHeight();
		if (labelsUpperTrostRunde != null)
			normalRoundHeight -= 4 * labelHeight / 2;
		if (labelsLowerTrostRunde != null)
			normalRoundHeight -= 5 * labelHeight / 2;

		if (labelsUpperTrostRunde != null)
			yOffset = 5 * labelHeight / 2;

		int i = 0;
		while (i < labels.length) {
			yPos += normalRoundHeight / (labelsPerColumn + 1);
			if (labelInColumn % 2 == 0) {
				yPos += labelHeight / 4;
			} else {
				yPos -= labelHeight / 4;
			}
			if (i < 15) {
				labels[i].setBounds(xPos, yPos + yOffset, labelWidth, labelHeight);
			} else {
				labels[i].setBounds(xPos - FighterDojoLabel.dojoNameSize, yPos + yOffset, labelWidth
						+ FighterDojoLabel.dojoNameSize, labelHeight);
			}
			if (i == 0) {
				labelFinal.setBounds(xPos, yPos + yOffset - (int) (1.0 * labelHeight), labelWidth, labelHeight);
			}

			labelInColumn += 1;
			if (labelInColumn >= labelsPerColumn) {
				labelInColumn = 0;
				labelsPerColumn *= 2;
				yPos = -labelHeight * 3 / 4;
				if (i != 0) {
					xPos = xPos - columnWidth;
				} else {
					xPos = xPos - labelPaddingX;
				}
			}
			i++;
		}

		// Trostrunde
		if (labelsUpperTrostRunde != null) {
			int depthTR = labelsUpperTrostRunde.length / 2;
			int columnWidthTR = (parent.getWidth() - borderLeftRight * 2) / (depthTR - 0);
			int labelWidthTR = columnWidthTR - labelPaddingX;
			layoutContainerTrostRunde(parent, true, labelsUpperTrostRunde, columnWidthTR, labelWidthTR, labelHeight,
					borderLeftRight);
		}

		if (labelsLowerTrostRunde != null) {
			int depthTR = labelsLowerTrostRunde.length / 2;
			int columnWidthTR = (parent.getWidth() - borderLeftRight * 2) / (depthTR - 0);
			int labelWidthTR = columnWidthTR - labelPaddingX;
			layoutContainerTrostRunde(parent, false, labelsLowerTrostRunde, columnWidthTR, labelWidthTR, labelHeight,
					borderLeftRight);
		}

		for (i = 0; i < connections.size(); i++) {
			((Component) connections.get(i)).setBounds(0, 0, parent.getWidth(), parent.getHeight());
		}

	}

	private void layoutContainerTrostRunde(Container parent, boolean upper, JComponent[] labelsTrostRunde,
			int columnWidth, int labelWidthTR, int labelHeight, int borderLeftRight) {
		if (labelsTrostRunde != null) {
			int i = 0;
			// first column is special
			int yPos;
			int xPos = parent.getWidth() - labelWidthTR - borderLeftRight;
			if (upper) {
				yPos = 3 * labelHeight / 2 + 10;
				labelUpperTrostrunde.setBounds(xPos + labelWidthTR / 3, yPos + (int) (2.5 * labelHeight), labelWidthTR,
						labelHeight);
			} else {
				yPos = parent.getHeight() - 5 * labelHeight / 2 - 10;
				labelLowerTrostrunde.setBounds(xPos + labelWidthTR / 3, yPos - (int) (2.5 * labelHeight), labelWidthTR,
						labelHeight);
			}
			labelsTrostRunde[i++].setBounds(xPos, yPos, labelWidthTR, labelHeight);
			xPos -= labelPaddingX;
			yPos -= 3 * labelHeight / 2;
			labelsTrostRunde[i++].setBounds(xPos, yPos, labelWidthTR, labelHeight);
			yPos += 3 * labelHeight;
			labelsTrostRunde[i++].setBounds(xPos, yPos, labelWidthTR, labelHeight);

			if (upper) {
				yPos -= 3 * labelHeight / 2;
			}

			// for (int j = 2; j < depth ; j++) {
			while (i < labelsTrostRunde.length) {
				xPos = xPos - columnWidth;
				labelsTrostRunde[i++].setBounds(xPos, yPos - 3 * labelHeight / 2, labelWidthTR, labelHeight);
				labelsTrostRunde[i++].setBounds(xPos, yPos, labelWidthTR, labelHeight);
			}
		}
	}

	private static class Connection1 extends JComponent {
		private JComponent[] labels;

		public Connection1(JComponent[] labels) {
			this.labels = labels;
		}

		@Override
		public void paintComponent(Graphics g) {
			if (labels[0] == null || labels[1] == null || labels[2] == null)
				return;

			int x1 = labels[0].getX() + labels[0].getWidth();
			int x5 = labels[2].getX();
			int y1 = labels[0].getY() + labels[0].getHeight() / 2;
			int y2 = labels[1].getY() + labels[1].getHeight() / 2;
			int ym = (y1 + y2) / 2;
			int y3 = labels[2].getY() + labels[2].getHeight() / 2;
			int x2 = x1 + 5;
			int x4 = x5 - 5;
			int x3 = (x2 + x4) / 2;

			g.setColor(Color.BLACK);
			g.drawLine(x1, y1, x2, y1);
			g.drawLine(x1, y2, x2, y2);
			g.drawLine(x2, y1, x2, y2);
			g.drawLine(x2, ym, x3, ym);
			g.drawLine(x3, ym, x3, y3);
			g.drawLine(x3, y3, x5, y3);
		}

	}

	private static class Connection2 extends JComponent {
		private JComponent[] labels;

		public Connection2(JComponent[] labels) {
			this.labels = labels;
		}

		@Override
		public void paintComponent(Graphics g) {
			if (labels[0] == null || labels[1] == null || labels[2] == null)
				return;

			int x1 = labels[1].getX() + labelPaddingX / 2;
			int x2 = labels[2].getX();
			int y1 = labels[0].getY() + labels[0].getHeight();
			int y2 = labels[1].getY();
			int y3 = labels[2].getY() + labels[2].getHeight() / 2;

			g.setColor(Color.BLACK);
			g.drawLine(x1, y1, x1, y2);
			g.drawLine(x1, y3, x2, y3);
		}

	}

	public boolean isEndPanel() {
		return isEndPanel;
	}

	public void setEndPanel(boolean isEndPanel) {
		this.isEndPanel = isEndPanel;
		// CR 15: "Finalist" statt "Final - Blatt"
		labelFinal.setText(isEndPanel ? "Final" : "Finalist");
	}

	public boolean isEndPanelOfFinalrunde() {
		return isEndPanelOfFinalrunde;
	}

}
